/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2002-2006
* Sleepycat Software. All rights reserved.
*
* $Id: DBIN.java,v 1.1 2006/05/06 09:00:14 ckaestne Exp $
*/
package com.sleepycat.je.tree;
import java.nio.ByteBuffer;
import java.util.Comparator;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.dbi.CursorImpl;
import com.sleepycat.je.dbi.DatabaseId;
import com.sleepycat.je.dbi.DatabaseImpl;
import com.sleepycat.je.dbi.DbConfigManager;
import com.sleepycat.je.dbi.MemoryBudget;
import com.sleepycat.je.log.LogEntryType;
import com.sleepycat.je.log.LogException;
import com.sleepycat.je.log.LogUtils;
import com.sleepycat.je.log.LoggableObject;
/**
* A DBIN represents an Duplicate Bottom Internal Node in the JE tree.
*/
public final class DBIN extends BIN implements LoggableObject {
private static final String BEGIN_TAG = "<dbin>";
private static final String END_TAG = "</dbin>";
/**
* Full key for this set of duplicates.
*/
private byte[] dupKey;
public DBIN() {
super();
}
public DBIN(DatabaseImpl db,
byte[] identifierKey,
int maxEntriesPerNode,
byte[] dupKey,
int level) {
super(db, identifierKey, maxEntriesPerNode, level);
this.dupKey = dupKey;
}
/**
* Create a new DBIN. Need this because we can't call newInstance()
* without getting a 0 node.
*/
protected IN createNewInstance(byte[] identifierKey,
int maxEntries,
int level) {
return new DBIN(getDatabase(),
identifierKey,
maxEntries,
dupKey,
level);
}
/* Duplicates have no mask on their levels. */
protected int generateLevel(DatabaseId dbId, int newLevel) {
return newLevel;
}
/**
* Return the comparator function to be used for DBINs. This is
* the user defined duplicate comparison function, if defined.
*/
public final Comparator getKeyComparator() {
return getDatabase().getDuplicateComparator();
}
/**
* Return the key for this duplicate set.
*/
public byte[] getDupKey() {
return dupKey;
}
/**
* Get the key (dupe or identifier) in child that is used to locate
* it in 'this' node.
*/
public byte[] getChildKey(IN child)
throws DatabaseException {
return child.getIdentifierKey();
}
/*
* A DBIN uses the dupTree key in its searches.
*/
public byte[] selectKey(byte[] mainTreeKey, byte[] dupTreeKey) {
return dupTreeKey;
}
/**
* Return the key for navigating through the duplicate tree.
*/
public byte[] getDupTreeKey() {
return getIdentifierKey();
}
/**
* Return the key for navigating through the main tree.
*/
public byte[] getMainTreeKey() {
return dupKey;
}
/**
* @return true if this node is a duplicate-bearing node type, false
* if otherwise.
*/
public boolean containsDuplicates() {
return true;
}
/**
* @return the log entry type to use for bin delta log entries.
*/
LogEntryType getBINDeltaType() {
return LogEntryType.LOG_DUP_BIN_DELTA;
}
public BINReference createReference() {
return new DBINReference(getNodeId(), getDatabase().getId(),
getIdentifierKey(), dupKey);
}
/**
* Count up the memory usage attributable to this node alone.
*/
protected long computeMemorySize() {
long size = super.computeMemorySize();
/* XXX Need to update size when changing the dupKey.
if (dupKey != null && dupKey.getKey() != null) {
size += MemoryBudget.byteArraySize(dupKey.getKey().length);
}
*/
return size;
}
/* Called once at environment startup by MemoryBudget. */
public static long computeOverhead(DbConfigManager configManager)
throws DatabaseException {
/*
* Overhead consists of all the fields in this class plus the
* entry arrays in the IN class.
*/
return MemoryBudget.DBIN_FIXED_OVERHEAD +
IN.computeArraysOverhead(configManager);
}
protected long getMemoryOverhead(MemoryBudget mb) {
return mb.getDBINOverhead();
}
/*
* A DBIN cannot be the ancestor of any IN.
*/
protected boolean canBeAncestor(boolean targetContainsDuplicates) {
return false;
}
/**
* @Override
*/
boolean hasNonLNChildren() {
return false;
}
/**
* The following four methods access the correct fields in a
* cursor depending on whether "this" is a BIN or DBIN. For
* BIN's, the CursorImpl.index and CursorImpl.bin fields should be
* used. For DBIN's, the CursorImpl.dupIndex and CursorImpl.dupBin
* fields should be used.
*/
BIN getCursorBIN(CursorImpl cursor) {
return cursor.getDupBIN();
}
BIN getCursorBINToBeRemoved(CursorImpl cursor) {
return cursor.getDupBINToBeRemoved();
}
int getCursorIndex(CursorImpl cursor) {
return cursor.getDupIndex();
}
void setCursorBIN(CursorImpl cursor, BIN bin) {
cursor.setDupBIN((DBIN) bin);
}
void setCursorIndex(CursorImpl cursor, int index) {
cursor.setDupIndex(index);
}
/*
* Depth first search through a duplicate tree looking for an LN that
* has nodeId. When we find it, set location.bin and index and return
* true. If we don't find it, return false.
*
* No latching is performed.
*/
boolean matchLNByNodeId(TreeLocation location, long nodeId)
throws DatabaseException {
for (int i = 0; i < getNEntries(); i++) {
LN ln = (LN) fetchTarget(i);
if (ln != null) {
if (ln.getNodeId() == nodeId) {
location.bin = this;
location.index = i;
location.lnKey = getKey(i);
location.childLsn = getLsn(i);
return true;
}
}
}
return false;
}
/*
* DbStat support.
*/
void accumulateStats(TreeWalkerStatsAccumulator acc) {
acc.processDBIN(this, new Long(getNodeId()), getLevel());
}
public String beginTag() {
return BEGIN_TAG;
}
public String endTag() {
return END_TAG;
}
/**
* For unit test support:
* @return a string that dumps information about this IN, without
*/
public String dumpString(int nSpaces, boolean dumpTags) {
StringBuffer sb = new StringBuffer();
sb.append(TreeUtils.indent(nSpaces));
sb.append(beginTag());
sb.append('\n');
sb.append(TreeUtils.indent(nSpaces+2));
sb.append("<dupkey>");
sb.append(dupKey == null ? "" : Key.dumpString(dupKey, 0));
sb.append("</dupkey>");
sb.append('\n');
sb.append(super.dumpString(nSpaces, false));
sb.append(TreeUtils.indent(nSpaces));
sb.append(endTag());
return sb.toString();
}
/*
* Logging support
*/
/**
* @see LoggableObject#getLogType
*/
public LogEntryType getLogType() {
return LogEntryType.LOG_DBIN;
}
/**
* @see LoggableObject#getLogSize
*/
public int getLogSize() {
int size = super.getLogSize(); // ancestors
size += LogUtils.getByteArrayLogSize(dupKey); // identifier key
return size;
}
/**
* @see LoggableObject#writeToLog
*/
public void writeToLog(ByteBuffer logBuffer) {
// ancestors
super.writeToLog(logBuffer);
// identifier key
LogUtils.writeByteArray(logBuffer, dupKey);
}
/**
* @see BIN#readFromLog
*/
public void readFromLog(ByteBuffer itemBuffer, byte entryTypeVersion)
throws LogException {
// ancestors
super.readFromLog(itemBuffer, entryTypeVersion);
// identifier key
dupKey = LogUtils.readByteArray(itemBuffer);
}
/**
* DBINS need to dump their dup key
*/
protected void dumpLogAdditional(StringBuffer sb) {
super.dumpLogAdditional(sb);
sb.append(Key.dumpString(dupKey, 0));
}
public String shortClassName() {
return "DBIN";
}
}